 import java.awt.*;
 import javax.swing.*;
 import java.awt.Graphics2D;

 /** 
 * La classe Node contiene le informazioni di un singolo 
 * nodo all'interno del grafo
 * 
 * @author Massimiliano Ferretti matricola 79769 
 */

 public class Node 
   implements java.io.Serializable
 {
 
  private int x;
  private int y;
  private final static int nodeRadius = 25;
   
  private int index;
  private boolean selected;
  private boolean preSelected;
  
  private Node previous;
  private int distance;
  
  /**
   * Restituisce il Nodo precedente relativo al cammino minimo corrente.
   * @return Il Nodo precedente, null se il cammino minimo non è stato ancora
   * calcolato.   
   */        
  public Node getPrevious()
  {
    return previous;
  }
  
  /**
   * Imposta il Nodo precedente.
   * @param node Il Nodo che precede quello corrente nel cammino minimo.
   */        
  public void setPrevious(Node node)
  {
    previous = node;
  }
  
  /**
   * Restituisce la distanza attuale del Nodo corrente dal Nodo di partenza.
   * @return Distanza del Nodo, se il cammino non è stato calcolato restituisce 
   * un valore Integer.MAX_VALUE che equivale ad una distanza infinita.   
   */        
  public int getDistance()
  {
    return distance;
  }
  
  /**
   * Imposta la distanza attuale del Nodo.
   * @param distance Nuova distanza del Nodo   
   */     
  public void setDistance(int distance)
  {
    this.distance = distance;
  }

  /**
   * Imposta la distanza attuale del Nodo a infinito.
   */     
  public void setDistanceToInfinite()
  {
    this.distance = Integer.MAX_VALUE;
  }
  
  /**
   * Restituisce se la distanza attuale è infinita o meno.
   * @return true se la distanza attuale è infinita, false altrimenti   
   */     
  public boolean isDistanceInfinite()
  {
    return (distance == Integer.MAX_VALUE);
  }
  
  /** 
   * Restituisce l'indice univoco del nodo
   * @return  Indice univoco del nodo all'interno del Grafo    
  */
  public int getIndex(){
    
    return index;
  }
  
  /**
   * Restituisce se il nodo è correntemente in pre-selezione
   * @return true se il nodo è pre-selezionato, false altrimenti   
   */     
   public boolean isPreSelected()
   {
    return preSelected;
   }
  
  /**
   * Controlla se la coordinata passata come parametro rientra nell'area 
   * del nodo, in caso positivo passa nello stato di pre-selezione.     
   * @param x Coordinata X della posizione del mouse
   * @param y Coordinata Y della posizione del mouse       
   */     
   public void preSelect(int x, int y)
   {
    preSelected = intersect(x, y);
   }
  
  /**
   * Restituisce se il nodo è correntemente selezionato
   * @return  true se il nodo è selezionato, false altrimenti   
   */     
  public boolean isSelected()
  {
    return selected;
  }
  
  /**
   * Controlla se la coordinata passata come parametro rientra nell'area
   * del nodo, in caso positivo passa nello stato di selezione   
   * @param x Coordinata X della posizione del mouse
   * @param y Coordinata Y della posizione del mouse       
   **/     
  public void select(int x, int y)
  {
    selected = intersect(x, y);
  }
  
  /**
   * Se il nodo si trova nello stato di selezione, aggiorna le coordinate
   * @param x Nuova coordinata X del nodo
   * @param y Nuova coordinata Y del nodo      
   */
  public void update(int x, int y)
  {
    if(isSelected())
    {
      this.x += x;
      this.y += y;
    }
  }     
  
  private boolean intersect(int x, int y)
  {
    return (x >= this.x && y >= this.y &&
       x <= this.x+(nodeRadius*2) && y <= this.y+(nodeRadius*2));
  }
  
  /**
   * Costruttore del Nodo
   * @param index L'indice univoco del Nodo, non potrà essere modificato successivamente
   * @param x Coordinata X iniziale del Nodo
   * @param y Coordinata Y iniziale del Nodo         
   * @see Graph#addNode(Point pos)   
   */     
  public Node(int index, int x, int y)
  {
    this.distance = Integer.MAX_VALUE;
    this.previous = null;
    this.index = index;
    this.x = x;
    this.y = y;
  }
  
  /**
   * Restituisce il raggio del Nodo.
   * @return Il raggio del Nodo (default 50)   
   */        
  public static int Radius()
  {
    return nodeRadius;
  }
  
  /**
   * Restituisce le coordinate del nodo
   * @return Le coordinate del nodo
   */
  public Point getPos()
  {
    return new Point(x, y);
  }        
  
  public void paint(Graphics2D g)
  {
    // Salva le impostazioni
    Color defaultColor = g.getColor();
    Stroke defaultStroke = g.getStroke();
    Font defaultFont = g.getFont();
  
    paintNode(g);
    paintNodeIndex(g);
    paintDistance(g);
    
    // Reimposta le impostazioni originali
    g.setFont(defaultFont);
    g.setStroke(defaultStroke);
    g.setColor(defaultColor);    
  }   
  
  private void paintNode(Graphics2D g)
  {
    // Draw Node
    g.setColor(Color.LIGHT_GRAY);
    g.fillOval(x, y, nodeRadius*2, nodeRadius*2);
    
    if(isSelected())
      g.setColor(Color.GREEN);
    else if(isPreSelected())
      g.setColor(Color.YELLOW);
    else
      g.setColor(Color.BLACK);
      
    g.drawOval(x, y, nodeRadius*2, nodeRadius*2);  
  }
  
  private void paintNodeIndex(Graphics2D g)
  {
    // Scrive l'indice del nodo
    Font font = new Font("Verdana", Font.ITALIC, 12);
    String text = Integer.toString(index);
    FontMetrics metrics = g.getFontMetrics(font);
    int h = metrics.getHeight();
    int w = metrics.stringWidth(text);
    
    g.setColor(Color.BLACK);
    g.setFont(font);
    g.drawString(text, x + (nodeRadius) - (w / 2), y + (nodeRadius));  
  }
  
  private void paintDistance(Graphics2D g)
  {
    // Scrive la distanza
    final int ovalRadius = 20;
    Point ovalCoord = new Point(x + nodeRadius, y + nodeRadius + 5); 
    Color color = Color.BLUE;
    String text = "";
    Font font = new Font("Verdana", Font.ITALIC, 12);
    FontMetrics metrics = g.getFontMetrics(font);
    
    if(isDistanceInfinite())
    {
      text = "inf";
      color = Color.RED;      
    }else
      text = Integer.toString(distance);
      
    int h = metrics.getHeight();
    int w = metrics.stringWidth(text);
    g.setColor(color);      
    g.fillOval(ovalCoord.x, ovalCoord.y, ovalRadius, ovalRadius);
    g.setColor(Color.WHITE);
    g.drawString(text, ovalCoord.x + (ovalRadius/2 - w/2), ovalCoord.y + (ovalRadius - h/2));  
  }
 }
